home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume8 / sp / part01 next >
Encoding:
Internet Message Format  |  1987-02-19  |  44.5 KB

  1. Subject:  v08i076:  Soundex spelling-checker, Part01/02
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: Barry Brachman <brachman@cs.ubc.cdn>
  6. Mod.sources: Volume 8, Issue 76
  7. Archive-name: sp/Part01
  8.  
  9.  
  10. Sp is a soundex-based dictionary search program that uses the
  11. dbm routines.  Included are Makefiles, man pages, and an Mlisp interface.
  12.  
  13. Barry Brachman
  14. Dept. of Computer Science
  15. Univ. of British Columbia
  16. Vancouver, B.C. V6T 1W5
  17.  
  18. .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
  19. brachman@cs.ubc.cdn
  20. brachman%ubc.csnet@csnet-relay.arpa
  21. brachman@ubc.csnet
  22.  
  23. #! /bin/sh
  24. # This is a shell archive.  Remove anything before this line,
  25. # then unpack it by saving it in a file and typing "sh file".
  26. # If all goes well, you will see the message "End of archive 1 (of 2)."
  27. # Contents:  README Makefile Makefile.newdbm calcsoundex.c dbm.bug
  28. #   dbm.diffs dbmstuff.c misc.c mksp.c sp.1 MANIFEST
  29. PATH=/bin:/usr/bin:/usr/ucb; export PATH
  30. echo shar: extracting "'README'" '(5017 characters)'
  31. if test -f 'README' ; then 
  32.   echo shar: will not over-write existing file "'README'"
  33. else
  34. sed 's/^X//' >README <<'@//E*O*F README//'
  35. X
  36. XHere are a pair of programs that might be of some use to those who have
  37. Xtrouble with spelling.
  38. X
  39. XThe first program, sp, accepts your tentative or approximate
  40. Xspelling of a word as input and produces a list of words.
  41. XIf the correct spelling of the word appears in one of the dictionaries used,
  42. Xit is likely that it appears in the output list.
  43. XNote that this is different from the UNIX 'spell' command that
  44. Xtells you which words in a document do not appear in the dictionary.
  45. X
  46. XThe second program, mksp, lets you maintain your own dictionary of troublesome
  47. Xwords.
  48. X
  49. X=====
  50. XTo run sp you'll need:
  51. X    - the Unix dbm routines, old or new (4.3BSD)
  52. X
  53. XNot required, but very useful:
  54. X    - the source to the old dbm routines if you don't have the new ones
  55. X      or your dbm routines don't have dbmclose() (check your man page for
  56. X      dbm(3X) to see if you've got dbmclose())
  57. X    - /usr/dict/words plus any other large list of words you might have
  58. X
  59. X=====
  60. XI apologize for the complexity of the following guide.  It is due to the
  61. Xpossibility of 4 different dbm configurations:  4.3 style dbm, Sun style dbm
  62. Xwith the dbmclose() routine, "old" (4.2BSD/V7) dbm with source and without
  63. Xsource.
  64. X
  65. X1. The program assumes that a char is 8 bits and an int is at least 16 bits.
  66. X   I've avoided using shorts.
  67. X
  68. X2. Note the following if you are using the old dbm routines that *don't* have
  69. X   dbmclose():
  70. X   The "old" dbm routines that don't have dbmclose() don't work properly if you
  71. X   do more than one dbminit().  If you have source code, you can apply the
  72. X   diffs so that multiple dbminit() calls will work, allowing
  73. X   multiple dictionaries to be used by sp, although you can still only access
  74. X   one dbm at a time.  If you do not have source then you can still use
  75. X   sp/mksp except you must change MAXDICT (in sp.h) to 1 and edit
  76. X   Makefile.newdbm as indicated there.  You will only be able to use one
  77. X   dictionary.  I'm including a bug report that came off the net for the old
  78. X   dbm routines.  This bug patch has been included in dbm.diffs but is
  79. X   surrounded by #ifdef BUGFIX.
  80. X
  81. X   If you're applying the patches to the old dbm code, make a copy of dbm.c
  82. X   and dbm.h.  Apply the patches by:
  83. X    patch < dbm.diffs
  84. X   or by hand (Larry Wall's patch program is in the mod.sources archive).
  85. X
  86. X3. Note the following if you are using the old dbm routines that *do* have
  87. X   dbmclose() (e.g., Sun 2 and Sun 3):
  88. X   Edit Makefile.newdbm and uncomment the two lines indicated.  Make using
  89. X   Makefile.newdbm (see below).
  90. X
  91. X4. Check sp.h and adjust for local conditions.  You might also edit sp.1
  92. X   to reflect your local configuration.
  93. X
  94. X5. I've tried to make it easy to change the key used for retrieving from
  95. X   the dbm.  The routines to make and disassemble a key are in misc.c.
  96. X   I want to keep the key as small as possible since dictionaries tend to
  97. X   be rather large.  I've used a vector of unsigned chars for the key because
  98. X   I didn't want to have to deal with various lengths of shorts and ints on
  99. X   different hardware.
  100. X
  101. X6. If you are using the "new" dbm routines (e.g., those in 4.3BSD that allow
  102. X   multiple simultaneously open dbm's), if you have dbmclose(), or if you have
  103. X   the old dbm routines without the dbm source then do:
  104. X    make -f Makefile.newdbm
  105. X   otherwise do:
  106. X       make
  107. X
  108. X   Then move sp, mksp, and calcsoundex to a public directory.  Copy sp.1 to
  109. X   where you keep man pages for such programs (you might also link mksp.1 and
  110. X   calcsoundex.1 to sp.1).
  111. X
  112. X7. If you are using Gosling EMACS, copy sp.ml (the MLISP interface to sp) into
  113. X   a public EMACS library.  I haven't tried to convert sp.ml to work with
  114. X   gnuemacs.  Put the documentation (sp.9) where appropriate on your system
  115. X   (you may need to edit the FILES section).
  116. X
  117. X8. You should create a public library using /usr/dict/words, e.g.:
  118. X    mksp -a -v /usr/public/lib/sp.dict < /usr/dict/words
  119. X   The path of this dictionary should appear in DEFAULT_SPPATH (sp.h). Users
  120. X   should be made aware of the public version so they don't make their own copy.
  121. X
  122. X9. dbm doesn't seem to work between a Sun and VAX across NFS.  Too bad.
  123. X   (It does work between Sun's.)
  124. X   Use rsh with the dictionary list on the command line.
  125. X
  126. X10. The programs have been tested on Sun 3/160 (4.2BSD 3.0), VAX 750 (4.3BSD),
  127. X    using both the new and old dbm routines.
  128. X
  129. X11. I have a dictionary of 35K words (350Kb) that do not appear in
  130. X    /usr/dict/words.  The only way I have of circulating it is on a
  131. X    double-sided Atari ST or Mac disk (single-sided if ARC'ed).  If you are
  132. X    interested send me a message.  Perhaps it could be archived somewhere
  133. X    (any volunteers?).
  134. X
  135. X12. Reference: Knuth, D.E. The Art of Computer Programming, Volume 3/Sorting
  136. X    and Searching, 1973, pp.391-392.
  137. X
  138. X13. If you find any bugs please notify me rather than posting to the net.
  139. X
  140. XEnjoy!
  141. X
  142. X-----
  143. XBarry Brachman
  144. XDept. of Computer Science
  145. XUniv. of British Columbia
  146. XVancouver, B.C. V6T 1W5
  147. X
  148. X.. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
  149. Xbrachman@cs.ubc.cdn
  150. Xbrachman%ubc.csnet@csnet-relay.arpa
  151. Xbrachman@ubc.csnet
  152. X
  153. @//E*O*F README//
  154. if test 5017 -ne "`wc -c <'README'`"; then
  155.     echo shar: error transmitting "'README'" '(should have been 5017 characters)'
  156. fi
  157. fi # end of overwriting check
  158. echo shar: extracting "'Makefile'" '(482 characters)'
  159. if test -f 'Makefile' ; then 
  160.   echo shar: will not over-write existing file "'Makefile'"
  161. else
  162. sed 's/^X//' >Makefile <<'@//E*O*F Makefile//'
  163. X
  164. X# Makefile for systems using modified dbm routines
  165. X
  166. XCFLAGS=-O
  167. X
  168. Xall:    sp mksp
  169. X
  170. Xsp:    sp.o dbmstuff.o dbm.o misc.o
  171. X    cc ${CFLAGS} -o sp sp.o dbmstuff.o misc.o dbm.o
  172. X
  173. Xmksp:    mksp.o dbmstuff.o dbm.o misc.o
  174. X    cc ${CFLAGS} -o mksp mksp.o dbmstuff.o misc.o dbm.o
  175. X
  176. Xcalcsoundex:    calcsoundex.c
  177. X    cc ${CFLAGS} -o calcsoundex calcsoundex.c
  178. X
  179. X# define BUGFIX if you want the fix included
  180. Xdbm.o:    dbm.h dbm.c
  181. X    cc -c ${CFLAGS} dbm.c
  182. X
  183. X.c.o:
  184. X    cc -c ${CFLAGS} $?
  185. X
  186. Xclean:
  187. X    rm -f sp.o mksp.o dbmstuff.o dbm.o
  188. X
  189. @//E*O*F Makefile//
  190. if test 482 -ne "`wc -c <'Makefile'`"; then
  191.     echo shar: error transmitting "'Makefile'" '(should have been 482 characters)'
  192. fi
  193. fi # end of overwriting check
  194. echo shar: extracting "'Makefile.newdbm'" '(850 characters)'
  195. if test -f 'Makefile.newdbm' ; then 
  196.   echo shar: will not over-write existing file "'Makefile.newdbm'"
  197. else
  198. sed 's/^X//' >Makefile.newdbm <<'@//E*O*F Makefile.newdbm//'
  199. X
  200. X# Makefile for systems with the new dbm routines (e.g., 4.3BSD systems),
  201. X# those with a dbm library containing dbmclose() (e.g., Sun 2 and Sun 3),
  202. X# and those with the old dbm without source
  203. X
  204. XCFLAGS=-O -DNEWDBM
  205. XLIB=
  206. X
  207. X# If you are using the old dbm routines with dbmclose(), uncomment
  208. X# the following two lines (otherwise comment them out)
  209. X# CFLAGS=-O -DHAS_CLOSE
  210. X# LIB=-ldbm
  211. X
  212. X# If you are using the old dbm routines without dbmclose(), uncomment
  213. X# the following two lines (otherwise comment them out)
  214. X# CFLAGS=-O
  215. X# LIB=-ldbm
  216. X
  217. Xall:    sp mksp
  218. X
  219. Xsp:    sp.o dbmstuff.o misc.o sp.h
  220. X    cc ${CFLAGS} -o sp sp.o dbmstuff.o misc.o ${LIB}
  221. X
  222. Xmksp:    mksp.o dbmstuff.o sp.h
  223. X    cc ${CFLAGS} -o mksp mksp.o dbmstuff.o misc.o ${LIB}
  224. X
  225. Xcalcsoundex:    calcsoundex.c
  226. X    cc ${CFLAGS} -o calcsoundex calcsoundex.c
  227. X
  228. X.c.o:
  229. X    cc -c ${CFLAGS} $?
  230. X
  231. Xclean:
  232. X    rm -f sp.o mksp.o dbmstuff.o dbm.o
  233. X
  234. @//E*O*F Makefile.newdbm//
  235. if test 850 -ne "`wc -c <'Makefile.newdbm'`"; then
  236.     echo shar: error transmitting "'Makefile.newdbm'" '(should have been 850 characters)'
  237. fi
  238. fi # end of overwriting check
  239. echo shar: extracting "'calcsoundex.c'" '(2455 characters)'
  240. if test -f 'calcsoundex.c' ; then 
  241.   echo shar: will not over-write existing file "'calcsoundex.c'"
  242. else
  243. sed 's/^X//' >calcsoundex.c <<'@//E*O*F calcsoundex.c//'
  244. X/* vi: set tabstop=4 : */
  245. X
  246. X/*
  247. X * calcsoundex - calculate soundex codes
  248. X *
  249. X * Permission is given to copy or distribute this program provided you
  250. X * do not remove this header or make money off of the program.
  251. X *
  252. X * Please send comments and suggestions to:
  253. X * Barry Brachman
  254. X * Dept. of Computer Science
  255. X * Univ. of British Columbia
  256. X * Vancouver, B.C. V6T 1W5
  257. X *
  258. X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
  259. X * brachman@cs.ubc.cdn
  260. X * brachman%ubc.csnet@csnet-relay.arpa
  261. X * brachman@ubc.csnet
  262. X */
  263. X
  264. X#include <stdio.h>
  265. X#include <ctype.h>
  266. X
  267. X#include "sp.h"
  268. X
  269. Xchar word[MAXWORDLEN + 2];
  270. X
  271. Xchar soundex_code_map[26] = {
  272. X/***     A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P    ***/ 
  273. X         0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 1,
  274. X
  275. X/***     Q  R  S  T  U  V  W  X  Y  Z            ***/
  276. X         2, 6, 2, 3, 0, 1, 0, 2, 0, 2
  277. X};
  278. X
  279. Xmain(argc, argv)
  280. Xint argc;
  281. Xchar **argv;
  282. X{
  283. X    register int c, i, soundex_length, digit_part, previous_code;
  284. X    int ch, len, vflag;
  285. X    short soundex;
  286. X    char *gets();
  287. X
  288. X    vflag = 0;
  289. X    if (argc > 2 || (argc == 2 && strcmp(argv[1], "-v"))) {
  290. X        fprintf(stderr, "Usage: calcsoundex [-v]\n");
  291. X        exit(1);
  292. X    }
  293. X    if (argc > 1)
  294. X        vflag = 1;
  295. X
  296. X    while (fgets(word, sizeof(word), stdin) != (char *) NULL) {
  297. X        len = strlen(word);
  298. X        if (word[len - 1] != '\n') {
  299. X            fprintf(stderr, "calcsoundex: Word too long: %s", word);
  300. X            while ((ch = getchar()) != '\n')    /* flush rest of line */
  301. X                putc(ch, stderr);
  302. X            putc('\n', stderr);
  303. X            continue;
  304. X        }
  305. X        word[--len] = '\0';
  306. X        if (len > MAXWORDLEN) {
  307. X            fprintf(stderr, "calcsoundex: Word too long: %s\n", word);
  308. X            continue;
  309. X        }
  310. X
  311. X        for (i = 0; word[i] != '\0'; i++) {
  312. X            if (isupper(word[i]))
  313. X                word[i] = tolower(word[i]);
  314. X        }
  315. X        if (!isalpha(word[0]))
  316. X            continue;
  317. X
  318. X        digit_part = 0;
  319. X        soundex_length = 0;
  320. X        previous_code = soundex_code_map[word[0] - 'a'];
  321. X        for (i = 1; word[i] != '\0' && soundex_length < 3; i++) {
  322. X            if (!isalpha(word[i]))
  323. X                continue;
  324. X            c = soundex_code_map[word[i] - 'a'];
  325. X            if (c == 0 || previous_code == c) {
  326. X                previous_code = c;
  327. X                continue;
  328. X            }
  329. X            digit_part = digit_part * 10 + c;
  330. X            previous_code = c;
  331. X            soundex_length++;
  332. X        }
  333. X        while (soundex_length++ < 3)
  334. X            digit_part *= 10;
  335. X        soundex = digit_part << 5 + word[0] - 'a';
  336. X        printf("%c", word[0]);
  337. X        if (digit_part < 100)
  338. X            putchar('0');
  339. X        if (digit_part < 10)
  340. X            putchar('0');
  341. X        if (digit_part == 0)
  342. X            putchar('0');
  343. X        else
  344. X            printf("%d", digit_part);
  345. X        if (vflag)
  346. X            printf(" %s", word);
  347. X        putchar('\n');
  348. X    }
  349. X    putchar('\n');
  350. X    exit(0);
  351. X}
  352. X
  353. @//E*O*F calcsoundex.c//
  354. if test 2455 -ne "`wc -c <'calcsoundex.c'`"; then
  355.     echo shar: error transmitting "'calcsoundex.c'" '(should have been 2455 characters)'
  356. fi
  357. fi # end of overwriting check
  358. echo shar: extracting "'dbm.bug'" '(1839 characters)'
  359. if test -f 'dbm.bug' ; then 
  360.   echo shar: will not over-write existing file "'dbm.bug'"
  361. else
  362. sed 's/^X//' >dbm.bug <<'@//E*O*F dbm.bug//'
  363. XArticle 770 of net.bugs.4bsd:
  364. XPath: ubc-cs!ubc-ean!alberta!ihnp4!mhuxn!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!elsie!ado
  365. XFrom: ado@elsie.UUCP (Arthur David Olson)
  366. XSubject: 4.?bsd dbm's store(k,c) dies if (i=k.dsize+c.dsize)==1018||i==1019--FIX
  367. XDate: Wed, 10-Apr-85 09:02:36 PST
  368. XDate-Received: Thu, 11-Apr-85 13:03:49 PST
  369. XOrganization: NIH-LEC, Bethesda, MD
  370. X
  371. XIndex:        lib/libdbm/dbm.c Fix
  372. X
  373. XDescription:
  374. X    4.?bsd dbm's store function misbehaves if the sum of the key data
  375. X    size and content data size is either 1018 or 1019.
  376. X
  377. XRepeat-By:
  378. X    Compile this program with the "dbm" library:
  379. X
  380. X        typedef struct {
  381. X            char *    dptr;
  382. X            int    dsize;
  383. X        } datum;
  384. X
  385. X        char    buf[1024];
  386. X
  387. X        main(argc, argv)
  388. X        int    argc;
  389. X        char *    argv[];
  390. X        {
  391. X            int    result;
  392. X            datum    key;
  393. X            datum    content;
  394. X
  395. X            key.dptr = content.dptr = buf;
  396. X            key.dsize = atoi(argv[1]);
  397. X            content.dsize = 0;
  398. X            creat("fake.dir", 0600);
  399. X            creat("fake.pag", 0600);
  400. X            dbminit("fake");
  401. X            result = store(key, content);
  402. X            printf("%d\n", result);
  403. X        }
  404. X
  405. X    Then run the program.  If you use commands such as
  406. X        a.out 0
  407. X        a.out 1
  408. X        ...
  409. X        a.out 1017
  410. X    things go swimmingly.  If you use commands such as
  411. X        a.out 1019
  412. X        a.out 1020
  413. X        ...
  414. X    an error message is (correctly) produced.  But if you use either
  415. X    the command
  416. X        a.out 1018
  417. X    or
  418. X        a.out 1019
  419. X    things go wild.
  420. X
  421. XFix:
  422. X    As usual, the trade secret status of the code involved precludes a
  423. X    clearer posting.  The fix is to change one line in "dbm.c"; it
  424. X    causes an error message to be produced in the 1018/1019 cases:
  425. X
  426. X        #ifdef OLDVERSION
  427. X            if(key.dsize+dat.dsize+2*sizeof(short) >= PBLKSIZ) {
  428. X        #else
  429. X            if(key.dsize+dat.dsize+3*sizeof(short) >= PBLKSIZ) {
  430. X        #endif
  431. X--
  432. XBugs is a Warner Brothers trademark
  433. X--
  434. X    UUCP: ..decvax!seismo!elsie!ado    ARPA: elsie!ado@seismo.ARPA
  435. X    DEC, VAX and Elsie are Digital Equipment and Borden trademarks
  436. X
  437. X
  438. @//E*O*F dbm.bug//
  439. if test 1839 -ne "`wc -c <'dbm.bug'`"; then
  440.     echo shar: error transmitting "'dbm.bug'" '(should have been 1839 characters)'
  441. fi
  442. fi # end of overwriting check
  443. echo shar: extracting "'dbm.diffs'" '(2748 characters)'
  444. if test -f 'dbm.diffs' ; then 
  445.   echo shar: will not over-write existing file "'dbm.diffs'"
  446. else
  447. sed 's/^X//' >dbm.diffs <<'@//E*O*F dbm.diffs//'
  448. XIndex: dbm.c
  449. X*** dbm.orig.c    Thu Nov 27 17:45:38 1986
  450. X--- dbm.c    Thu Nov 27 18:13:11 1986
  451. X***************
  452. X*** 6,16 ****
  453. X--- 6,21 ----
  454. X  #include    <sys/types.h>
  455. X  #include    <sys/stat.h>
  456. X  
  457. X+ static long dbm_access_oldb;
  458. X+ static getbit_oldb;
  459. X+ 
  460. X  dbminit(file)
  461. X  char *file;
  462. X  {
  463. X      struct stat statb;
  464. X  
  465. X+     dbm_access_oldb = -1;
  466. X+     getbit_oldb = -1;
  467. X      dbrdonly = 0;
  468. X      strcpy(pagbuf, file);
  469. X      strcat(pagbuf, ".pag");
  470. X***************
  471. X*** 27,36 ****
  472. X          dirf = open(pagbuf, 0);
  473. X          dbrdonly = 1;
  474. X      }
  475. X!     if(pagf < 0 || dirf < 0) {
  476. X!         printf("cannot open database %s\n", file);
  477. X          return(-1);
  478. X-     }
  479. X      fstat(dirf, &statb);
  480. X      maxbno = statb.st_size*BYTESIZ-1;
  481. X      return(0);
  482. X--- 32,39 ----
  483. X          dirf = open(pagbuf, 0);
  484. X          dbrdonly = 1;
  485. X      }
  486. X!     if(pagf < 0 || dirf < 0)
  487. X          return(-1);
  488. X      fstat(dirf, &statb);
  489. X      maxbno = statb.st_size*BYTESIZ-1;
  490. X      return(0);
  491. X***************
  492. X*** 130,136 ****
  493. X--- 133,143 ----
  494. X      return (0);
  495. X  
  496. X  split:
  497. X+ #ifdef BUGFIX
  498. X+     if(key.dsize+dat.dsize+3*sizeof(short) >= PBLKSIZ) {
  499. X+ #else
  500. X      if(key.dsize+dat.dsize+2*sizeof(short) >= PBLKSIZ) {
  501. X+ #endif
  502. X          printf("entry too big\n");
  503. X          return (-1);
  504. X      }
  505. X***************
  506. X*** 226,232 ****
  507. X  dbm_access(hash)
  508. X  long hash;
  509. X  {
  510. X!     static long oldb = -1;
  511. X  
  512. X      for(hmask=0;; hmask=(hmask<<1)+1) {
  513. X          blkno = hash & hmask;
  514. X--- 233,239 ----
  515. X  dbm_access(hash)
  516. X  long hash;
  517. X  {
  518. X! /***    static long oldb = -1;    ***/
  519. X  
  520. X      for(hmask=0;; hmask=(hmask<<1)+1) {
  521. X          blkno = hash & hmask;
  522. X***************
  523. X*** 234,245 ****
  524. X          if(getbit() == 0)
  525. X              break;
  526. X      }
  527. X!     if(blkno != oldb) {
  528. X          clrbuf(pagbuf, PBLKSIZ);
  529. X          lseek(pagf, blkno*PBLKSIZ, 0);
  530. X          read(pagf, pagbuf, PBLKSIZ);
  531. X          chkblk(pagbuf);
  532. X!         oldb = blkno;
  533. X      }
  534. X  }
  535. X  
  536. X--- 241,252 ----
  537. X          if(getbit() == 0)
  538. X              break;
  539. X      }
  540. X!     if(blkno != dbm_access_oldb) {
  541. X          clrbuf(pagbuf, PBLKSIZ);
  542. X          lseek(pagf, blkno*PBLKSIZ, 0);
  543. X          read(pagf, pagbuf, PBLKSIZ);
  544. X          chkblk(pagbuf);
  545. X!         dbm_access_oldb = blkno;
  546. X      }
  547. X  }
  548. X  
  549. X***************
  550. X*** 247,253 ****
  551. X  {
  552. X      long bn;
  553. X      register b, i, n;
  554. X!     static oldb = -1;
  555. X  
  556. X      if(bitno > maxbno)
  557. X          return(0);
  558. X--- 254,260 ----
  559. X  {
  560. X      long bn;
  561. X      register b, i, n;
  562. X! /***    static oldb = -1;  ***/
  563. X  
  564. X      if(bitno > maxbno)
  565. X          return(0);
  566. X***************
  567. X*** 255,265 ****
  568. X      bn = bitno / BYTESIZ;
  569. X      i = bn % DBLKSIZ;
  570. X      b = bn / DBLKSIZ;
  571. X!     if(b != oldb) {
  572. X          clrbuf(dirbuf, DBLKSIZ);
  573. X          lseek(dirf, (long)b*DBLKSIZ, 0);
  574. X          read(dirf, dirbuf, DBLKSIZ);
  575. X!         oldb = b;
  576. X      }
  577. X      if(dirbuf[i] & (1<<n))
  578. X          return(1);
  579. X--- 262,272 ----
  580. X      bn = bitno / BYTESIZ;
  581. X      i = bn % DBLKSIZ;
  582. X      b = bn / DBLKSIZ;
  583. X!     if(b != getbit_oldb) {
  584. X          clrbuf(dirbuf, DBLKSIZ);
  585. X          lseek(dirf, (long)b*DBLKSIZ, 0);
  586. X          read(dirf, dirbuf, DBLKSIZ);
  587. X!         getbit_oldb = b;
  588. X      }
  589. X      if(dirbuf[i] & (1<<n))
  590. X          return(1);
  591. @//E*O*F dbm.diffs//
  592. if test 2748 -ne "`wc -c <'dbm.diffs'`"; then
  593.     echo shar: error transmitting "'dbm.diffs'" '(should have been 2748 characters)'
  594. fi
  595. fi # end of overwriting check
  596. echo shar: extracting "'dbmstuff.c'" '(1595 characters)'
  597. if test -f 'dbmstuff.c' ; then 
  598.   echo shar: will not over-write existing file "'dbmstuff.c'"
  599. else
  600. sed 's/^X//' >dbmstuff.c <<'@//E*O*F dbmstuff.c//'
  601. X/* dbmstuff.c */
  602. X
  603. X/*
  604. X * Interface to old and new dbm routines
  605. X */
  606. X
  607. X#include <stdio.h>
  608. X
  609. X#ifndef NEWDBM
  610. X
  611. X#include <dbm.h>
  612. X
  613. X/*ARGSUSED*/
  614. XDBMINIT(path, flags)
  615. Xchar *path;
  616. Xint flags;
  617. X{
  618. X
  619. X    return(dbminit(path));
  620. X}
  621. X
  622. XDBMCLOSE()
  623. X{
  624. X
  625. X#ifdef HAS_CLOSE
  626. X    dbmclose();
  627. X#else
  628. X    close(3);        /* free up the file descriptors */
  629. X    close(4);
  630. X#endif
  631. X}
  632. X
  633. Xdatum
  634. XFETCH(key)
  635. Xdatum key;
  636. X{
  637. X    datum fetch();
  638. X
  639. X    return(fetch(key));
  640. X}
  641. X
  642. Xdatum
  643. XFIRSTKEY()
  644. X{
  645. X
  646. X    return(firstkey());
  647. X}
  648. X
  649. Xdatum
  650. XNEXTKEY(key)
  651. Xdatum key;
  652. X{
  653. X    return(nextkey(key));
  654. X}
  655. X
  656. XSTORE(key, content)
  657. Xdatum key, content;
  658. X{
  659. X
  660. X    return(store(key, content));
  661. X}
  662. X
  663. XREPLACE(key, content)
  664. Xdatum key, content;
  665. X{
  666. X
  667. X    if (delete(key) == -1)
  668. X        return(-1);
  669. X    return(store(key, content));
  670. X}
  671. X
  672. XDELETE(key)
  673. Xdatum key;
  674. X{
  675. X
  676. X    return(delete(key));
  677. X}
  678. X
  679. X#endif !NEWDBM
  680. X
  681. X#ifdef NEWDBM
  682. X
  683. X#include <ndbm.h>
  684. X
  685. Xstatic DBM *current_db = (DBM *) NULL;
  686. X
  687. XDBMINIT(path, flags)
  688. Xchar *path;
  689. Xint flags;
  690. X{
  691. X
  692. X    current_db = dbm_open(path, flags, 0);
  693. X    if (current_db == (DBM *) NULL)
  694. X        return(-1);
  695. X    return(0);
  696. X}
  697. X
  698. XDBMCLOSE()
  699. X{
  700. X
  701. X    if (current_db != (DBM *) NULL) {
  702. X        dbm_close(current_db);
  703. X        current_db = (DBM *) NULL;
  704. X    }
  705. X}
  706. X
  707. Xdatum
  708. XFETCH(key)
  709. Xdatum key;
  710. X{
  711. X
  712. X    return(dbm_fetch(current_db, key));
  713. X}
  714. X
  715. Xdatum
  716. XFIRSTKEY()
  717. X{
  718. X
  719. X    return(dbm_firstkey(current_db));
  720. X}
  721. X
  722. X/*ARGSUSED*/
  723. Xdatum
  724. XNEXTKEY(key)
  725. Xdatum key;
  726. X{
  727. X
  728. X    return(dbm_nextkey(current_db));
  729. X}
  730. X
  731. XREPLACE(key, content)
  732. Xdatum key, content;
  733. X{
  734. X
  735. X    return(dbm_store(current_db, key, content, DBM_REPLACE)); 
  736. X}
  737. X
  738. XSTORE(key, content)
  739. Xdatum key, content;
  740. X{
  741. X
  742. X    return(dbm_store(current_db, key, content, DBM_INSERT));
  743. X}
  744. X
  745. XDELETE(key)
  746. Xdatum key;
  747. X{
  748. X
  749. X    return(dbm_delete(current_db, key));
  750. X}
  751. X
  752. X#endif NEWDBM
  753. @//E*O*F dbmstuff.c//
  754. if test 1595 -ne "`wc -c <'dbmstuff.c'`"; then
  755.     echo shar: error transmitting "'dbmstuff.c'" '(should have been 1595 characters)'
  756. fi
  757. fi # end of overwriting check
  758. echo shar: extracting "'misc.c'" '(3643 characters)'
  759. if test -f 'misc.c' ; then 
  760.   echo shar: will not over-write existing file "'misc.c'"
  761. else
  762. sed 's/^X//' >misc.c <<'@//E*O*F misc.c//'
  763. X/* misc.c */
  764. X
  765. X/* vi: set tabstop=4 : */
  766. X
  767. X#include <ctype.h>
  768. X#include <stdio.h>
  769. X
  770. X#include "sp.h"
  771. X
  772. X/*
  773. X * Special character map that determines what the second character of a word
  774. X * can be; see sp.h
  775. X * May be expanded to contain up to 12 entries plus the terminating entry
  776. X * Must end with an entry of two null bytes
  777. X */
  778. Xstruct spchar_map spchar_map[] = {
  779. X    '\'',    QUOTE_CHAR,
  780. X    '&',    AMPER_CHAR,
  781. X    '.',    PERIOD_CHAR,
  782. X    ' ',    SPACE_CHAR,
  783. X    '\0',    '\0'
  784. X};
  785. X
  786. Xmk_key(key, soundex, count)
  787. Xkey_t *key;
  788. Xint soundex;
  789. Xint count;
  790. X{
  791. X
  792. X    key[0] = soundex & 0377;
  793. X    key[1] = ((soundex & 037400) >> 8) | ((count & 03) << 6);
  794. X    key[2] = (count & 01774) >> 2;
  795. X#ifdef DEBUG
  796. X    if (ex_soundex(key) != soundex)
  797. X        fprintf(stderr, "mk_key: soundex failed\n");
  798. X    if (ex_count(key) != count)
  799. X        fprintf(stderr, "mk_key: count failed\n");
  800. X#endif DEBUG
  801. X}
  802. X
  803. Xex_soundex(key)
  804. Xkey_t *key;
  805. X{
  806. X    register int soundex;
  807. X
  808. X    soundex = key[0] & 0377;
  809. X    soundex |= (key[1] & 077) << 8;
  810. X    return(soundex);
  811. X}
  812. X
  813. Xex_count(key)
  814. Xkey_t *key;
  815. X{
  816. X    register int count;
  817. X
  818. X    count = (key[1] & 0300) >> 6;
  819. X    count |= ((key[2] & 0377) << 2);
  820. X    return(count);
  821. X}
  822. X
  823. X/*
  824. Xex_char(key)
  825. Xkey_t *key;
  826. X{
  827. X    int ch;
  828. X
  829. X    ch = (key[1] & 076) >> 1;
  830. X    return(ch + 'a');
  831. X}
  832. X*/
  833. X
  834. X/*
  835. X * Unpack a word given the retrieved word of length len and its soundex
  836. X * Extract the first letter from the soundex code
  837. X * If the length is 1 and if it is marked as a single character word
  838. X * then the marked character will be overlaid with a null
  839. X * otherwise a null will be appended to the string
  840. X * Adjust for upper case leading character if necessary
  841. X * Return address of the copy
  842. X */
  843. Xchar *
  844. Xmk_word(p, len, s)
  845. Xchar *p;
  846. Xint len, s;
  847. X{
  848. X    register char *q, ch;
  849. X    static char word[MAXWORDLEN + 2];
  850. X
  851. X    q = word;
  852. X    if (len == 1 && (*p & SINGLE_CHAR)) {
  853. X        *(q + 1) = '\0';
  854. X        len = 0;
  855. X    }
  856. X    else
  857. X        *(q + len + 1) = '\0';
  858. X
  859. X    /*
  860. X     * Extract the first character from the soundex and
  861. X     * adjust case
  862. X     */
  863. X    if (*p & UPPER_CHAR)
  864. X        ch = (s & 037) + 'A';
  865. X    else
  866. X        ch = (s & 037) + 'a';
  867. X    *q++ = ch;
  868. X
  869. X    if (len != 0) {                /* if more than one char adjust second char */
  870. X        ch = *p & MASK_CHAR;
  871. X        if (ch < 26)
  872. X            ch += 'a';
  873. X        else if (ch < 52)
  874. X            ch = ch - 26 + 'A';
  875. X        else if ((ch = fromspchar(ch)) == '\0') {
  876. X            fprintf(stderr, "Bogus second char in mk_word\n");
  877. X            exit(1);
  878. X        }
  879. X        *q++ = ch;
  880. X        p++;
  881. X        len--;
  882. X    }
  883. X
  884. X    while (len-- > 0)
  885. X        *q++ = *p++;
  886. X    return(word);
  887. X}
  888. X
  889. X/*
  890. X * Convert the second character of a word to a special character code
  891. X * Return null if there is no mapping
  892. X */
  893. Xtospchar(ch)
  894. Xchar ch;
  895. X{
  896. X    register struct spchar_map *m;
  897. X
  898. X    for (m = spchar_map; m->spchar != '\0'; m++)
  899. X        if (ch == m->spchar)
  900. X            break;
  901. X    return(m->code);
  902. X}
  903. X
  904. X/*
  905. X * Convert from the special character code to the ASCII code
  906. X * Return null if there is no mapping
  907. X */
  908. Xfromspchar(ch)
  909. Xchar ch;
  910. X{
  911. X    register struct spchar_map *m;
  912. X
  913. X    for (m = spchar_map; m->spchar != '\0'; m++)
  914. X        if (ch == m->code)
  915. X            break;
  916. X    return(m->spchar);
  917. X}
  918. X
  919. X/*
  920. X * Compare two strings, independent of case, given their lengths
  921. X */
  922. X/*
  923. Xstrnmatch(str1, len1, str2, len2)
  924. Xchar *str1, *str2;
  925. Xint len1, len2;
  926. X{
  927. X    register char ch1, ch2;
  928. X
  929. X    if (len1 != len2)
  930. X        return(0);
  931. X    while (len1-- > 0) {
  932. X        ch1 = *str1++;
  933. X        ch2 = *str2++;
  934. X        if (ch1 != ch2) {
  935. X            if (isupper(ch1))
  936. X                ch1 = tolower(ch1);
  937. X            if (isupper(ch2))
  938. X                ch2 = tolower(ch2);
  939. X            if (ch1 != ch2)
  940. X                return(0);
  941. X        }
  942. X    }
  943. X    return(1);
  944. X}
  945. X*/
  946. X
  947. X/*
  948. X * Compare two strings, independent of case
  949. X */
  950. Xstrmatch(p, q)
  951. Xchar *p, *q;
  952. X{
  953. X    register char ch1, ch2;
  954. X
  955. X    while (1) {
  956. X        ch1 = *p++;
  957. X        ch2 = *q++;
  958. X        if (ch1 == '\0' || ch2 == '\0')
  959. X            break;
  960. X        if (ch1 != ch2) {
  961. X            if (isupper(ch1))
  962. X                ch1 = tolower(ch1);
  963. X            if (isupper(ch2))
  964. X                ch2 = tolower(ch2);
  965. X            if (ch1 != ch2)
  966. X                break;
  967. X        }
  968. X    }
  969. X    return(ch1 - ch2);
  970. X}
  971. X
  972. @//E*O*F misc.c//
  973. if test 3643 -ne "`wc -c <'misc.c'`"; then
  974.     echo shar: error transmitting "'misc.c'" '(should have been 3643 characters)'
  975. fi
  976. fi # end of overwriting check
  977. echo shar: extracting "'mksp.c'" '(12797 characters)'
  978. if test -f 'mksp.c' ; then 
  979.   echo shar: will not over-write existing file "'mksp.c'"
  980. else
  981. sed 's/^X//' >mksp.c <<'@//E*O*F mksp.c//'
  982. X/* vi: set tabstop=4 : */
  983. X
  984. X/*
  985. X * mksp - make soundex dictionary
  986. X * Version 1.3,  December 1986
  987. X *
  988. X * If <soundexfile.{dir, pag}> do not exist, try to create them
  989. X * If they do exist and are not empty, then words will be added
  990. X * from the standard input
  991. X * Only when words are being added to an existing database are duplicate words
  992. X * ignored
  993. X * Valid words (words beginning with an alphabetic) are stored as given but
  994. X * comparisons for duplicates is case independent.
  995. X * Non-alphabetic characters are ignored in computing the soundex
  996. X *
  997. X * Permission is given to copy or distribute this program provided you
  998. X * do not remove this header or make money off of the program.
  999. X *
  1000. X * Please send comments and suggestions to:
  1001. X * Barry Brachman
  1002. X * Dept. of Computer Science
  1003. X * Univ. of British Columbia
  1004. X * Vancouver, B.C. V6T 1W5
  1005. X *
  1006. X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
  1007. X * brachman@cs.ubc.cdn
  1008. X * brachman%ubc.csnet@csnet-relay.arpa
  1009. X * brachman@ubc.csnet
  1010. X */
  1011. X
  1012. X#include <ctype.h>
  1013. X#include <errno.h>
  1014. X#include <sys/types.h>
  1015. X#include <sys/file.h>
  1016. X#include <sys/stat.h>
  1017. X#include <stdio.h>
  1018. X
  1019. X#ifdef NEWDBM
  1020. X#include <ndbm.h>
  1021. X#include <sys/file.h>
  1022. X#else !NEWDBM
  1023. X#include <dbm.h>
  1024. X#endif NEWDBM
  1025. X
  1026. X#include "sp.h"
  1027. X
  1028. X#define NEW_DICT            0
  1029. X#define OLD_DICT            1
  1030. X
  1031. X#define VFREQ                1000    /* frequency for verbose messages */
  1032. X
  1033. X#define streq(X, Y)            (!strcmp(X, Y))
  1034. X#define strneq(X, Y, N)        (!strncmp(X, Y, N))
  1035. X
  1036. X#define USAGE                "Usage: mksp -tad [-v[#]] <soundexfile>"
  1037. X
  1038. Xint map[26][667];
  1039. X
  1040. Xdatum FETCH(), FIRSTKEY(), NEXTKEY();
  1041. X
  1042. Xkey_t keyvec[KEYSIZE];
  1043. Xkey_t *key = keyvec;
  1044. X
  1045. X/*
  1046. X * Soundex codes
  1047. X * The program depends upon the numbers zero through six being used
  1048. X * but this can easily be changed
  1049. X */
  1050. Xchar soundex_code_map[26] = {
  1051. X/***     A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P    ***/ 
  1052. X         0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, 1,
  1053. X
  1054. X/***     Q  R  S  T  U  V  W  X  Y  Z            ***/
  1055. X         2, 6, 2, 3, 0, 1, 0, 2, 0, 2
  1056. X};
  1057. X
  1058. Xint digit_part;
  1059. X
  1060. Xint aflag, dflag, tflag, vflag;
  1061. X
  1062. Xchar *mk_word();
  1063. X
  1064. Xmain(argc, argv)
  1065. Xint argc;
  1066. Xchar **argv;
  1067. X{
  1068. X    register int i;
  1069. X    char *file;
  1070. X
  1071. X    if (argc != 3 && argc != 4) {
  1072. X        fprintf(stderr, "%s\n", USAGE);
  1073. X        exit(1);
  1074. X    }
  1075. X    aflag = dflag = tflag = vflag = 0;
  1076. X    file = (char *) NULL;
  1077. X
  1078. X    for (i = 1; i < argc; i++) {
  1079. X        if (streq(argv[i], "-a"))
  1080. X            aflag = 1;
  1081. X        else if (streq(argv[i], "-d"))
  1082. X            dflag = 1;
  1083. X        else if (streq(argv[i], "-t"))
  1084. X            tflag = 1;
  1085. X        else if (strneq(argv[i], "-v", 2)) {
  1086. X            if (isdigit(argv[i][2]))
  1087. X                vflag = atoi(&argv[i][2]);
  1088. X            else
  1089. X                vflag = 1;
  1090. X        }
  1091. X        else if (file == (char *) NULL)
  1092. X            file = argv[i];
  1093. X        else {
  1094. X            fprintf(stderr, "%s\n", USAGE);
  1095. X            exit(1);
  1096. X        }
  1097. X    }
  1098. X
  1099. X    if (file == (char *) NULL || (tflag + aflag + dflag) != 1) {
  1100. X        fprintf(stderr, "%s\n", USAGE);
  1101. X        exit(1);
  1102. X    }
  1103. X
  1104. X    if (aflag) {
  1105. X        addwords(file);
  1106. X        if (vflag > 1) {
  1107. X            register int j, total;
  1108. X            int m, max;
  1109. X
  1110. X            fprintf(stderr, "Counters:\n");
  1111. X            for (i = 0; i < 26; i++) {
  1112. X                total = max = map[i][0];
  1113. X                for (j = 1; j < 667; j++) {
  1114. X                    total += (m = map[i][j]);
  1115. X                    if (m > max)
  1116. X                        max = m;
  1117. X                }
  1118. X                if (max > 0)
  1119. X                    fprintf(stderr, "%c: max %d total %d\n", 'a'+i, max, total);
  1120. X            }
  1121. X        }
  1122. X    }
  1123. X    else if (dflag)
  1124. X        deletewords(file);
  1125. X    else if (tflag)
  1126. X        prcontents(file);
  1127. X    exit(0);
  1128. X}
  1129. X
  1130. X/*
  1131. X * Add words read from stdin to the database
  1132. X * The key is the 3 digit soundex code for the word plus a disambiguating
  1133. X * counter.  Different counter values are used for words with the same soundex
  1134. X * code.  The maximum counter value is MAXCOUNT.  If the counter overflows then
  1135. X * we lose, but given at least 8 bits this seems unlikely.
  1136. X */
  1137. Xaddwords(name)
  1138. Xchar *name;
  1139. X{
  1140. X    register int c, count, delete, duplicate, i, len;
  1141. X    register int *p;
  1142. X    int ch, s, status;
  1143. X    char wcopy[MAXWORDLEN + 2], word[MAXWORDLEN + 2];
  1144. X    datum dbm_key, dbm_content;
  1145. X
  1146. X    status = setup(name);
  1147. X    if (DBMINIT(name, O_RDWR) == -1) {
  1148. X        fprintf(stderr, "mksp: Can't initialize\n");
  1149. X        exit(1);
  1150. X    }
  1151. X
  1152. X    for (i = 0; i < 26; i++)
  1153. X        for (c = 0; c < 667; c++)
  1154. X            map[i][c] = 0;
  1155. X
  1156. X    dbm_key.dptr = (char *) key;
  1157. X    dbm_key.dsize = KEYSIZE;
  1158. X
  1159. X    count = 0;
  1160. X
  1161. X    while (fgets(word, sizeof(word), stdin) != (char *) NULL) {
  1162. X        len = strlen(word);
  1163. X        if (word[len - 1] != '\n') {
  1164. X            fprintf(stderr, "mksp: Word too long: %s", word);
  1165. X            while ((ch = getchar()) != '\n')    /* flush rest of line */
  1166. X                putc(ch, stderr);
  1167. X            putc('\n', stderr);
  1168. X            continue;
  1169. X        }
  1170. X        word[--len] = '\0';
  1171. X        if (len > MAXWORDLEN) {
  1172. X            fprintf(stderr, "mksp: Word too long: %s\n", word);
  1173. X            continue;
  1174. X        }
  1175. X
  1176. X        if ((s = soundex(word, 3)) == BAD_WORD) {
  1177. X            if (vflag)
  1178. X                fprintf(stderr, "Ignoring bad word: %s\n", word);
  1179. X            continue;
  1180. X        }
  1181. X        ch = (isupper(word[0]) ? tolower(word[0]) : word[0]) - 'a';
  1182. X        p = &(map[ch][digit_part]);
  1183. X
  1184. X        /*
  1185. X         * If words are being added to an old dictionary,
  1186. X         * check for duplication and watch for a deleted entry
  1187. X         * The reason for only checking for duplicates in old dictionaries is
  1188. X         * that usually when you're creating a new dictionary the words are
  1189. X         * already sorted and unique and the creation of a large dictionary is
  1190. X         * slow enough already.
  1191. X         */
  1192. X        duplicate = 0;
  1193. X        delete = -1;            /* an 'impossible' counter */
  1194. X        if (status == OLD_DICT) {
  1195. X            c = 0;
  1196. X            while (1) {
  1197. X                mk_key(key, s, c);
  1198. X                dbm_content = FETCH(dbm_key);
  1199. X                if (dbm_content.dptr == 0)
  1200. X                    break;
  1201. X
  1202. X                if (!IS_DELETED(dbm_content)) {
  1203. X                    char *str;
  1204. X
  1205. X                    str = mk_word(dbm_content.dptr, dbm_content.dsize, s);
  1206. X                    if (strmatch(word, str) == 0) {
  1207. X                        duplicate = 1;
  1208. X                        if (vflag)
  1209. X                            fprintf(stderr, "duplicate: %s\n", word);
  1210. X                        break;
  1211. X                    }
  1212. X                }
  1213. X                else if (delete < 0)    /* choose delete nearest front */
  1214. X                    delete = c;
  1215. X
  1216. X                if (++c > MAXCOUNT) {
  1217. X                    fprintf(stderr, "mksp: Counter overflow\n");
  1218. X                    fprintf(stderr, "soundex: %c%d\n", ch+'a', b10(digit_part));
  1219. X                    exit(1);
  1220. X                }
  1221. X            }
  1222. X            if (duplicate)
  1223. X                continue;
  1224. X            *p = c;
  1225. X        }
  1226. X        if (*p > MAXCOUNT) {
  1227. X            fprintf(stderr, "mksp: Counter overflow\n");
  1228. X            fprintf(stderr, "soundex: %c%d\n", ch+'a', b10(digit_part));
  1229. X            exit(1);
  1230. X        }
  1231. X        mk_key(key, s, *p);
  1232. X        *p = *p + 1;
  1233. X        strcpy(wcopy, word);
  1234. X        if (len == 1) {
  1235. X            if (isupper(wcopy[0]))
  1236. X                wcopy[0] |= UPPER_CHAR;
  1237. X            wcopy[0] |= SINGLE_CHAR;
  1238. X            dbm_content.dptr = wcopy;
  1239. X        }
  1240. X        else {
  1241. X            if (isupper(wcopy[1]))
  1242. X                wcopy[1] = wcopy[1] - 'A' + 26;
  1243. X            else if (islower(wcopy[1]))
  1244. X                wcopy[1] = wcopy[1] - 'a';
  1245. X            else if ((wcopy[1] = tospchar(wcopy[1])) == '\0') {
  1246. X                fprintf(stderr, "Bogus second char: can't happen!\n");
  1247. X                exit(1);
  1248. X            }
  1249. X            if (isupper(wcopy[0]))
  1250. X                wcopy[1] = wcopy[1] | UPPER_CHAR;
  1251. X            dbm_content.dptr = wcopy + 1;
  1252. X            len--;
  1253. X        }
  1254. X        dbm_content.dsize = len;                    /* null not stored */
  1255. X        if (delete < 0) {
  1256. X            if (STORE(dbm_key, dbm_content) == -1) {
  1257. X                fprintf(stderr, "mksp: Can't store\n");
  1258. X                exit(1);
  1259. X            }
  1260. X        }
  1261. X        else {
  1262. X            if (vflag)
  1263. X                fprintf(stderr, "reusing: %s\n", word);
  1264. X            mk_key(key, s, delete);
  1265. X            if (REPLACE(dbm_key, dbm_content) == -1) {
  1266. X                fprintf(stderr, "mksp: Can't replace\n");
  1267. X                exit(1);
  1268. X            }
  1269. X        }
  1270. X        count++;
  1271. X        if (vflag > 1)
  1272. X            fprintf(stderr, "%5d: %s(%d)\n", count, word, ex_count(key));
  1273. X        if (vflag && (count % VFREQ) == 0)
  1274. X            fprintf(stderr, "%5d: %s\n", count, word);
  1275. X    }
  1276. X    if (vflag)
  1277. X        fprintf(stderr, "%d words\n", count);
  1278. X    DBMCLOSE();
  1279. X}
  1280. X
  1281. X/*
  1282. X * Print out everything
  1283. X */
  1284. Xprcontents(name)
  1285. Xchar *name;
  1286. X{
  1287. X    register int s;
  1288. X    datum dbm_key, dbm_content;
  1289. X
  1290. X    if (DBMINIT(name, O_RDONLY) == -1)
  1291. X        exit(1);
  1292. X
  1293. X    dbm_key = FIRSTKEY();
  1294. X    while (dbm_key.dptr != NULL) {
  1295. X        dbm_content = FETCH(dbm_key);
  1296. X        if (dbm_content.dptr == 0)
  1297. X            break;                        /* ??? */
  1298. X
  1299. X        if (vflag)
  1300. X            printf("%3d. ", ex_count((key_t *) dbm_key.dptr));
  1301. X        if (IS_DELETED(dbm_content)) {
  1302. X            if (vflag)
  1303. X                printf("(deleted)\n");
  1304. X        }
  1305. X        else {
  1306. X            s = ex_soundex((key_t *) dbm_key.dptr);
  1307. X            printf("%s\n", mk_word(dbm_content.dptr, dbm_content.dsize, s));
  1308. X        }
  1309. X        dbm_key = NEXTKEY(dbm_key);
  1310. X    }
  1311. X    DBMCLOSE();
  1312. X}
  1313. X
  1314. X/*
  1315. X * When words are deleted they must be marked as such rather than deleted
  1316. X * using DELETE().  This is because the sequence of counters must remain
  1317. X * continuous.  If DELETE() is used then any entries with the same soundex
  1318. X * but with a larger counter value would not be accessible.  This approach
  1319. X * does cost some extra space but if an addition is made to the chain then
  1320. X * a deleted counter slot will be reused.  Also, the storage used by the word
  1321. X * should be made available to dbm.  This could be improved somewhat
  1322. X * by actually using DELETE() on the last entry of the chain.
  1323. X */
  1324. Xdeletewords(name)
  1325. Xchar *name;
  1326. X{
  1327. X    register int c, ch, len, s;
  1328. X    register char *p;
  1329. X    char word[MAXWORDLEN + 2];
  1330. X    datum dbm_key, dbm_content;
  1331. X
  1332. X    if (DBMINIT(name, O_RDWR) == -1)
  1333. X        exit(1);
  1334. X
  1335. X    while (fgets(word, sizeof(word), stdin) != (char *) NULL) {
  1336. X        len = strlen(word);
  1337. X        if (word[len - 1] != '\n') {
  1338. X            fprintf(stderr, "mksp: Word too long: %s", word);
  1339. X            while ((ch = getchar()) != '\n')    /* flush rest of line */
  1340. X                putc(ch, stderr);
  1341. X            putc('\n', stderr);
  1342. X            continue;
  1343. X        }
  1344. X        word[--len] = '\0';
  1345. X        if (len > MAXWORDLEN) {
  1346. X            fprintf(stderr, "mksp: Word too long: %s\n", word);
  1347. X            continue;
  1348. X        }
  1349. X
  1350. X        if ((s = soundex(word, 3)) == BAD_WORD) {
  1351. X            if (vflag)
  1352. X                fprintf(stderr, "Bad word: %s\n", word);
  1353. X            continue;
  1354. X        }
  1355. X
  1356. X        c = 0;
  1357. X        while (1) {
  1358. X            dbm_key.dptr = (char *) key;
  1359. X            dbm_key.dsize = KEYSIZE;
  1360. X            mk_key(key, s, c);
  1361. X            dbm_content = FETCH(dbm_key);
  1362. X            if (dbm_content.dptr == NULL) {
  1363. X                if (vflag)
  1364. X                    fprintf(stderr, "Not found: %s\n", word);
  1365. X                break;
  1366. X            }
  1367. X
  1368. X            if (!IS_DELETED(dbm_content)) {
  1369. X                p = mk_word(dbm_content.dptr, dbm_content.dsize, s);
  1370. X                if (strmatch(word, p) == 0) {
  1371. X                    /*
  1372. X                     * Aside:
  1373. X                     * Since dptr points to static storage it must be reset
  1374. X                     * if we want to retain the old content (content.dptr=word)
  1375. X                     * This took a while to determine...
  1376. X                     * Anyhow, since there is no need to store the old word
  1377. X                     * we free up the space
  1378. X                     */
  1379. X                    dbm_content.dptr = "";
  1380. X                    dbm_content.dsize = 0;
  1381. X                    if (REPLACE(dbm_key, dbm_content) == -1)
  1382. X                        fprintf(stderr, "mksp: delete of '%s' failed\n", word);
  1383. X                    else if (vflag) {
  1384. X                        if (vflag > 1)
  1385. X                            fprintf(stderr, "%d. %s ", c, p);
  1386. X                        fprintf(stderr, "deleted\n");
  1387. X                    }
  1388. X                    break;
  1389. X                }
  1390. X                else if (vflag > 1)
  1391. X                    fprintf(stderr, "%d. %s\n", c, p);
  1392. X            }
  1393. X            else if (vflag > 1)
  1394. X                fprintf(stderr, "%d. (deleted)\n", c);
  1395. X
  1396. X            if (++c > MAXCOUNT) {
  1397. X                ch = isupper(word[0]) ? tolower(word[0]) : word[0];
  1398. X                fprintf(stderr, "mksp: Counter overflow\n");
  1399. X                fprintf(stderr, "soundex: %c%d\n", ch, b10(digit_part));
  1400. X                exit(1);
  1401. X            }
  1402. X        }
  1403. X    }
  1404. X    DBMCLOSE();
  1405. X}
  1406. X
  1407. X/*
  1408. X * Setup the dictionary files if necessary
  1409. X */
  1410. Xsetup(name)
  1411. Xchar *name;
  1412. X{
  1413. X    register int s1, s2;
  1414. X
  1415. X    s1 = check_dict(name, ".dir");
  1416. X    s2 = check_dict(name, ".pag");
  1417. X    if (s1 == NEW_DICT && s2 == NEW_DICT)
  1418. X        return(NEW_DICT);
  1419. X    return(OLD_DICT);
  1420. X}
  1421. X
  1422. X/*
  1423. X * Check if a dictionary file exists:
  1424. X *    - if not, try to create it
  1425. X *    - if so, see if it is empty
  1426. X * Return NEW_DICT if an empty file exists,
  1427. X * OLD_DICT if a non-empty file exists
  1428. X * Default mode for new files is 0666
  1429. X */
  1430. Xcheck_dict(name, ext)
  1431. Xchar *name, *ext;
  1432. X{
  1433. X    register int len, s;
  1434. X    char *filename;
  1435. X    struct stat statbuf;
  1436. X    char *malloc();
  1437. X    extern int errno;
  1438. X
  1439. X    len = strlen(name) + strlen(ext) + 1;
  1440. X    filename = (char *) malloc((unsigned) len);
  1441. X    if (filename == (char *) NULL) {
  1442. X        fprintf(stderr, "mksp: Can't malloc '%s.%s'\n", name, ext);
  1443. X        exit(1);
  1444. X    }
  1445. X    sprintf(filename, "%s%s", name, ext);
  1446. X    if (stat(filename, &statbuf) == -1) {
  1447. X        if (errno != ENOENT) {
  1448. X            perror("mksp");
  1449. X            exit(1);
  1450. X        }
  1451. X        if (creat(filename, 0666) == -1) {
  1452. X            perror("mksp");
  1453. X            exit(1);
  1454. X        }
  1455. X        s = NEW_DICT;
  1456. X    }
  1457. X    else {
  1458. X        if (statbuf.st_size == 0)
  1459. X            s = NEW_DICT;
  1460. X        else
  1461. X            s = OLD_DICT;
  1462. X    }
  1463. X    return(s);
  1464. X}
  1465. X
  1466. X/*
  1467. X * Compute an 'n' digit Soundex code for 'word' 
  1468. X * As a side effect, leave the digit part of the soundex in digit_part
  1469. X *
  1470. X * Since the soundex can be considered a base 7 number, if 'n' is:
  1471. X *    3    require  9 (10 if base 10) bits for digits
  1472. X *    4    require 12 (13) bits
  1473. X *    5    require 15 (17) bits
  1474. X *    6    require 17 (20) bits
  1475. X *
  1476. X * The three slightly different versions of this routine should be coalesced.
  1477. X */
  1478. Xsoundex(word, n)
  1479. Xregister char *word;
  1480. Xint n;
  1481. X{
  1482. X    register int c, soundex_length, previous_code;
  1483. X    register char *p, *w;
  1484. X    char wcopy[MAXWORDLEN + 2];
  1485. X
  1486. X    if (!IS_VALID(word))
  1487. X        return(-1);
  1488. X
  1489. X    strcpy(wcopy, word);
  1490. X    p = w = wcopy;
  1491. X
  1492. X    while (*p != '\0') {
  1493. X        if (isupper(*p))
  1494. X            *p = tolower(*p);
  1495. X        p++;
  1496. X    }
  1497. X
  1498. X    digit_part = 0;
  1499. X    soundex_length = 0;
  1500. X    previous_code = soundex_code_map[*w - 'a'];
  1501. X    for (p = w + 1; *p != '\0' && soundex_length < n; p++) {
  1502. X        if (!isalpha(*p))
  1503. X            continue;
  1504. X        c = soundex_code_map[*p - 'a'];
  1505. X        if (c == 0 || previous_code == c) {
  1506. X            previous_code = c;
  1507. X            continue;
  1508. X        }
  1509. X        digit_part = digit_part * 7 + c;
  1510. X        previous_code = c;
  1511. X        soundex_length++;
  1512. X    }
  1513. X    while (soundex_length++ < n)
  1514. X        digit_part *= 7;
  1515. X    return((digit_part << 5) + *w - 'a');
  1516. X}
  1517. X
  1518. Xb10(n)
  1519. Xint n;
  1520. X{
  1521. X    register int b10, s;
  1522. X
  1523. X    for (b10 = 0, s = 1; n != 0; n /= 7) {
  1524. X        b10 += (n % 7) * s;
  1525. X        s *= 10;
  1526. X    }
  1527. X    return(b10);
  1528. X}
  1529. X
  1530. @//E*O*F mksp.c//
  1531. if test 12797 -ne "`wc -c <'mksp.c'`"; then
  1532.     echo shar: error transmitting "'mksp.c'" '(should have been 12797 characters)'
  1533. fi
  1534. fi # end of overwriting check
  1535. echo shar: extracting "'sp.1'" '(5932 characters)'
  1536. if test -f 'sp.1' ; then 
  1537.   echo shar: will not over-write existing file "'sp.1'"
  1538. else
  1539. sed 's/^X//' >sp.1 <<'@//E*O*F sp.1//'
  1540. X.TH SP 1-LOCAL "11 December 1986"
  1541. X.UC 4
  1542. X.SH NAME
  1543. Xsp \- give possible spellings
  1544. X.br
  1545. Xmksp \- maintain sp dictionaries
  1546. X.br
  1547. Xcalcsoundex \- calculate soundex values
  1548. X.SH SYNOPSIS
  1549. X.B sp
  1550. X[
  1551. X.B -vace
  1552. X] [
  1553. X.B -f dictionary-list
  1554. X] [
  1555. X.B word ...
  1556. X]
  1557. X.br
  1558. X.B mksp
  1559. X[
  1560. X.B -adt
  1561. X] [
  1562. X.B -v#
  1563. X]
  1564. X.B dictionary
  1565. X.br
  1566. X.B calcsoundex
  1567. X[
  1568. X-v
  1569. X]
  1570. X.SH DESCRIPTION
  1571. X.I Sp
  1572. Xtakes one or more words as input and for each word prints
  1573. Xa list of possible spellings.
  1574. XIf the words are not given on the command line, the program prompts and reads
  1575. Xfrom the standard input.
  1576. XYou must know the first letter of the word.
  1577. XUpper case is mapped to lower case.
  1578. XWords must start with an alphabetic character, but any subsequent
  1579. Xcharacters need not be alphabetic (but see the limitation below on words that
  1580. Xmay appear in the dectionary).
  1581. XBlanks are allowed within a word.
  1582. X.PP
  1583. XUp to ten dictionaries previously created by
  1584. X.I mksp
  1585. Xmay be specified by a command line argument and an environment variable.
  1586. XThe name of a dictionary is specified by a pathname,
  1587. Xnot including the suffix (.dir or .pag).
  1588. XA list of dictionaries consists of one or more colon separated dictionary
  1589. Xnames.
  1590. XThe environment variable SPPATH may be set to a dictionary list.
  1591. XIf a command line dictionary list is given in addition to the SPPATH variable,
  1592. Xall dictionaries are used.
  1593. XIf no dictionaries are specified the program looks for default dictionaries.
  1594. X.PP
  1595. XTo reduce the size of the word list, certain heuristics are used.
  1596. XNormally, all words
  1597. X.I sp
  1598. Xconsiders to be a "satisfactory" match are printed.
  1599. XThe \fB-c\fR option causes only close
  1600. Xmatches or an exact match to be printed.
  1601. XThe \fB-e\fR option only prints exact matches.
  1602. XThe \fB-a\fR option causes all words matched to be printed.
  1603. XThe output is sorted alphabetically and indicators are printed beside each
  1604. Xword:
  1605. X.sp 2
  1606. X.in +0.5i
  1607. X.nf
  1608. X.na
  1609. X   X == exact match
  1610. X.br
  1611. X   ! == close match
  1612. X.br
  1613. X   * == good match
  1614. X.br
  1615. X ' ' == matched
  1616. X.in -0.5i
  1617. X.fi
  1618. X.ad
  1619. X.sp 2
  1620. XDuplicated words are not removed from the listing.
  1621. X.PP
  1622. XIf the \fB-v\fR flag is given,
  1623. X.I sp
  1624. Xbecomes verbose.
  1625. X.PP
  1626. X.I Mksp
  1627. Xis a program to maintain dictionaries for use with
  1628. X.I sp.
  1629. XThe \fB-a\fR option is used to create a new dictionary or to add
  1630. Xwords to an existing dictionary.
  1631. XThe words to be put in the dictionary are read from the standard input,
  1632. Xone per line.
  1633. XThe \fB-v\fR flag (which may immediately be followed by an optional number)
  1634. Xcauses some information to be printed as words are processed.
  1635. XA non-flag argument to
  1636. X.I mksp
  1637. Xis assumed to be the prefix of the name of the dictionary files.
  1638. XThe dictionary consists of two files, one with a ".dir" suffix and one with
  1639. Xa ".pag" suffix (see \fBdbm\fR(3X)).
  1640. XIf these files do not exist,
  1641. X.I mksp
  1642. Xwill create both.
  1643. XThe words need not be sorted.
  1644. XThere should not be duplicates in the word list when creating a new dictionary
  1645. Xbut when words are added to an existing dictionary, duplicates are ignored.
  1646. XUpper case letters are stored in the dictionary but
  1647. X.I mksp
  1648. Xmaps upper case to lower case when checking for duplicate words.
  1649. X.I Sp
  1650. Xis case insensitive when searching the database.
  1651. XThe first character of a word must be alphabetic and
  1652. Xthe second character (if present) must be either alphabetic, a single quote,
  1653. Xan ampersand, a period, or a blank.
  1654. XThere is no restriction on the third and subsequent characters.
  1655. X.PP
  1656. XThe \fB-d\fR option is used to delete words from the specified dictionary.
  1657. XThe words are read from the standard input.
  1658. XIf a word is not found in the dictionary, no message is printed.
  1659. XThe comparison is case insensitive.
  1660. X.PP
  1661. XThe \fB-t\fR option prints the contents of the specified dictionary.
  1662. XThe words are not sorted.
  1663. X.PP
  1664. X.I Calcsoundex
  1665. Xreads words from the standard input (one per line) and prints the soundex
  1666. Xcode corresponding to each word on stdout.
  1667. XWith the \fB-v\fR flag,
  1668. X.I calcsoundex
  1669. Xalso echoes each word.
  1670. X.SH EXAMPLE
  1671. X.in +0.5i
  1672. X.na
  1673. X.nf
  1674. X% mksp -a mydictionary
  1675. Xaardvark
  1676. Xprecipitation
  1677. X<more words>
  1678. Xzyzz
  1679. X<^D>
  1680. X% sp -c -f mydictionary:herdictionary
  1681. XWord? propogate
  1682. X!  1. perfect
  1683. X!  2. perfectible
  1684. X<more words>
  1685. X!  7. propagate
  1686. X!  8. proposition
  1687. X<more words>
  1688. XWord? <^D>
  1689. X.fi
  1690. X.ad
  1691. X.in -0.5i
  1692. X.SH FILES
  1693. X.nf
  1694. X.na
  1695. X<dictionary.pag>, <dictionary.dir>      dictionary data base
  1696. X/usr/local/lib/sp.dict.[12]             default dictionaries
  1697. X.fi
  1698. X.ad
  1699. X.SH LIMITATIONS
  1700. XNo more than 10 dictionaries may be specified.
  1701. XThe maximum length of a word is 50 characters.
  1702. XThe program can return up to 400 matches taking up a maximum of 20480 bytes.
  1703. XThe limitation on what the second character of a word can be is due to
  1704. Xthe algorithm used to compress the dictionaries; there is some room in the
  1705. Xdata structure for a few other characters to become valid.
  1706. XDbm doesn't work between a Sun and VAX across NFS so you can't share a
  1707. Xdictionary between a VAX and a Sun.
  1708. X.PP
  1709. XThere is a limit on the number of words having the same soundex code that can
  1710. Xappear in a single dictionary.
  1711. XThis value should be at least 256 (on a VAX/Sun it is 1024), but it depends
  1712. Xon how the program has been configured locally.
  1713. XIf you come up against this limitation you can split your dictionary; e.g.,
  1714. Xextract every second word from the big dictionary to make a new dictionary,
  1715. Xthen delete the words from the big dictionary.
  1716. XThe following pipeline can be used to determine the number of times each
  1717. Xsoundex code appears in a list of words (one per line):
  1718. X.sp 2
  1719. X.ti +0.5i
  1720. Xcalcsoundex | sort | uniq -c | sort -r -0n
  1721. X.SH "SEE ALSO"
  1722. Xspell(1), uniq(1), dbm(3X), ndbm(3)
  1723. X.br
  1724. XDonald E. Knuth, The Art of Computer Programming, Vol. 3,
  1725. XSorting and Searching, Addison-Wesley, pp. 391-392, 1973.
  1726. X.SH AUTHOR
  1727. XBarry Brachman
  1728. X.br
  1729. XDept. of Computer Science
  1730. X.br
  1731. XUniversity of British Columbia
  1732. X.SH BUGS
  1733. XYou may not agree on what constitutes a match.
  1734. XYou are likely to have to create your own dictionary as the UNIX
  1735. Xdictionary is far from complete.  In particular, the suffixes
  1736. Xhave been removed from most words.
  1737. XThe limitations mentioned above are arbitrary.
  1738. XThe limitation on the second character of a word is disgusting.
  1739. X
  1740. @//E*O*F sp.1//
  1741. if test 5932 -ne "`wc -c <'sp.1'`"; then
  1742.     echo shar: error transmitting "'sp.1'" '(should have been 5932 characters)'
  1743. fi
  1744. fi # end of overwriting check
  1745. echo shar: extracting "'MANIFEST'" '(1093 characters)'
  1746. if test -f 'MANIFEST' ; then 
  1747.   echo shar: will not over-write existing file "'MANIFEST'"
  1748. else
  1749. sed 's/^X//' >MANIFEST <<'@//E*O*F MANIFEST//'
  1750. X  File Name             Kit #   Description
  1751. X-----------------------------------------------------------
  1752. X MANIFEST                  1   This shipping list
  1753. X Makefile                  1   Makefile for use with old dbm routines and source
  1754. X Makefile.newdbm           1   Makefile for 4.3BSD dbm, dbmclose(), or old dbm w/o source
  1755. X README                    1   Configuration instructions, etc.
  1756. X calcsoundex.c             1   Program to calculate soundex codes
  1757. X dbm.bug                   1   A bug report for the old dbm routines
  1758. X dbm.diffs                 1   Diffs to be applied to old dbm routines
  1759. X dbmstuff.c                1   Interface to old and new dbm routines
  1760. X misc.c                    1   Miscellaneous support routines
  1761. X mksp.c                    1   Program to maintain dictionaries
  1762. X sp.1                      1   Man page for sp/mksp/calcsoundex
  1763. X sp.9                      2   Man page for EMACS interface to sp
  1764. X sp.c                      2   Program to search dictionaries
  1765. X sp.h                      2   Header file
  1766. X sp.ml                     2   Mlisp code for EMACS interface to sp
  1767. @//E*O*F MANIFEST//
  1768. if test 1093 -ne "`wc -c <'MANIFEST'`"; then
  1769.     echo shar: error transmitting "'MANIFEST'" '(should have been 1093 characters)'
  1770. fi
  1771. fi # end of overwriting check
  1772. echo shar: "End of archive 1 (of 2)."
  1773. cp /dev/null ark1isdone
  1774. DONE=true
  1775. for I in 1 2; do
  1776.     if test ! -f ark${I}isdone; then
  1777.         echo "You still need to run archive ${I}."
  1778.         DONE=false
  1779.     fi
  1780. done
  1781. case $DONE in
  1782.     true)
  1783.         echo "You have run both archives."
  1784.         echo 'See the README'
  1785.         ;;
  1786. esac
  1787. ##  End of shell archive.
  1788. exit 0
  1789.